package com.wnxy.sddfp.payback.service.impl;

import java.math.BigDecimal;
import java.time.LocalDateTime;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wnxy.sddfp.client.bank.TradeLogApi;
import com.wnxy.sddfp.common.constant.UserConstant;
import com.wnxy.sddfp.common.model.dto.bid.BankBidDTO;
import com.wnxy.sddfp.common.model.dto.payback.AgencyBidPayDTO;
import com.wnxy.sddfp.common.model.dto.payback.BorrowRecordPayDTO;
import com.wnxy.sddfp.common.model.dto.payback.PayBackDTO;
import com.wnxy.sddfp.common.result.Result;
import com.wnxy.sddfp.common.result.enums.impl.ResultCode;
import com.wnxy.sddfp.common.util.IdGenerator;
import com.wnxy.sddfp.payback.entity.AgencyReceiveItem;
import com.wnxy.sddfp.payback.entity.AgencyReceivePlan;
import com.wnxy.sddfp.payback.entity.BorrowerPayItem;
import com.wnxy.sddfp.payback.entity.BorrowerPayPlan;
import com.wnxy.sddfp.payback.mapper.AgencyReceiveItemMapper;
import com.wnxy.sddfp.payback.mapper.AgencyReceivePlanMapper;
import com.wnxy.sddfp.payback.mapper.BorrowerPayItemMapper;
import com.wnxy.sddfp.payback.mapper.BorrowerPayPlanMapper;
import com.wnxy.sddfp.payback.service.IBorrowerPayPlanService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wnxy.sddfp.payback.util.DEBXRepayVo;
import com.wnxy.sddfp.payback.util.DateUtil;
import com.wnxy.sddfp.payback.util.RepayCommon;
import lombok.extern.slf4j.Slf4j;
import org.mockito.internal.matchers.Find;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 借款人还款计划 服务实现类
 * </p>
 *
 * @author Jet
 * @since 2023-05-17
 */
@Service
@Slf4j
public class BorrowerPayPlanServiceImpl extends ServiceImpl<BorrowerPayPlanMapper, BorrowerPayPlan> implements IBorrowerPayPlanService {

    private final AgencyReceivePlanMapper agencyReceivePlanMapper;
    private final BorrowerPayItemMapper borrowerPayItemMapper;
    private final TradeLogApi tradeLogApi;
    private final AgencyReceiveItemMapper agencyReceiveItemMapper;

    public BorrowerPayPlanServiceImpl(
            AgencyReceivePlanMapper agencyReceivePlanMapper,
            BorrowerPayItemMapper borrowerPayItemMapper,
            TradeLogApi tradeLogApi,
            AgencyReceiveItemMapper agencyReceiveItemMapper) {
        this.agencyReceivePlanMapper = agencyReceivePlanMapper;
        this.borrowerPayItemMapper = borrowerPayItemMapper;
        this.tradeLogApi = tradeLogApi;
        this.agencyReceiveItemMapper = agencyReceiveItemMapper;
    }

    @Override
    public String payPlan(PayBackDTO payBackDTO) {
        //1. 生成借款人的还款计划 borrower_pay_plan
        //1.1 获取借款项目信息
        BorrowRecordPayDTO borrowRecordPayDTO = payBackDTO.getBorrowRecordPayDTO();
        //1.2 获取机构投资认购信息
        List<AgencyBidPayDTO> agencyBids = payBackDTO.getAgencyBids();
        //1.3 根据还款总天数，计算还款月数
        Integer period = borrowRecordPayDTO.getPeriod();
        Double temp = Math.ceil(period / 30);
        int month = temp.intValue();
        //1.4 生成还款计划
        DEBXRepayVo debxRepayVo = RepayCommon.fixedRepayment(
                borrowRecordPayDTO.getAmount(), borrowRecordPayDTO.getBorrowerRate(), month, borrowRecordPayDTO.getCommissionRate());
        //1.5 保存还款计划
        List<BorrowerPayPlan> payPlanList = savePayPlan(borrowRecordPayDTO, debxRepayVo);

        //2. 生成投资机构应收计划 agency_receive_plan
        //2.1 根据投标信息生成应收明细
        agencyBids.forEach(agencyBidDTO -> {
            DEBXRepayVo debxReceive = RepayCommon.fixedRepayment(
                    agencyBidDTO.getAmount(),
                    agencyBidDTO.getInvestRate(),
                    month,
                    payBackDTO.getInvestorAnnualRate());

            //2.2 保存应收明细到数据库
            payPlanList.forEach(borrowerPayPlan -> {
                saveAgencyReceivePlan(borrowerPayPlan, agencyBidDTO, debxReceive);
            });

        });

        return "OK";
    }

    /**
     * 保存还款计划
     */
    private List<BorrowerPayPlan> savePayPlan(
            BorrowRecordPayDTO borrowRecordPayDTO, DEBXRepayVo debxRepayVo) {
        // 返回结果
        List<BorrowerPayPlan> payPlanList = new ArrayList<>();

        // 每月还款利息, 期数:利息
        Map<Integer, BigDecimal> interestMap = debxRepayVo.getInterestMap();
        // 每月还款本金, 期数:本金
        Map<Integer, BigDecimal> principalMap = debxRepayVo.getPrincipalMap();
        // 平台抽息
        Map<Integer, BigDecimal> commissionMap = debxRepayVo.getCommissionMap();

        // 遍历每期还款本金map集合；key是期数；value是本金
        principalMap.forEach((period, money) -> {
            BorrowerPayPlan borrowerPayPlan = new BorrowerPayPlan();
            borrowerPayPlan.setUserId(borrowRecordPayDTO.getUserId());
            borrowerPayPlan.setUserCode(borrowRecordPayDTO.getUserCode());
            borrowerPayPlan.setBorrowRecordId(borrowRecordPayDTO.getId());
            borrowerPayPlan.setBorrowRecordNo(borrowRecordPayDTO.getBorrowRecordNo());
            borrowerPayPlan.setNumberOfPeriods(period);
            borrowerPayPlan.setInterest(interestMap.get(period)); // 还款利息
            borrowerPayPlan.setPrincipal(money);    // 还款本金
            borrowerPayPlan.setAmount(borrowerPayPlan.getPrincipal().add(borrowerPayPlan.getInterest())); // 本金 + 利息
            borrowerPayPlan.setShouldPayDate(DateUtil.localDateTimeAddMonth(LocalDateTime.now(), period)); // 应还时间 = 当前时间 + 期数(月)
            borrowerPayPlan.setPayStatus("0");
            borrowerPayPlan.setCreateDate(LocalDateTime.now());
            borrowerPayPlan.setCommission(commissionMap.get(period)); // 年化利率
            this.save(borrowerPayPlan); // 保存还款计划
            payPlanList.add(borrowerPayPlan);
        });
        return payPlanList;
    }

    /**
     * 生成投资人应收明细
     */
    private void saveAgencyReceivePlan(
            BorrowerPayPlan borrowerPayPlan, AgencyBidPayDTO agencyBidDTO, DEBXRepayVo debxRepayVo) {

        // 每月还款利息, 期数:利息
        Map<Integer, BigDecimal> interestMap = debxRepayVo.getInterestMap();
        // 每月还款本金, 期数:本金
        Map<Integer, BigDecimal> principalMap = debxRepayVo.getPrincipalMap();
        // 平台抽息
        Map<Integer, BigDecimal> commissionMap = debxRepayVo.getCommissionMap();

        // 封装应收明细
        AgencyReceivePlan agencyReceivePlan = new AgencyReceivePlan();
        agencyReceivePlan.setUserId(agencyBidDTO.getUserId());
        agencyReceivePlan.setUserCode(agencyBidDTO.getUserCode());
        agencyReceivePlan.setAgencyBidId(agencyBidDTO.getId());
        agencyReceivePlan.setPayPlanId(borrowerPayPlan.getId());
        agencyReceivePlan.setNumberOfPeriods(borrowerPayPlan.getNumberOfPeriods());
        agencyReceivePlan.setInterest(interestMap.get(borrowerPayPlan.getNumberOfPeriods()));
        agencyReceivePlan.setPrincipal(principalMap.get(borrowerPayPlan.getNumberOfPeriods()));
        agencyReceivePlan.setAmount(agencyReceivePlan.getInterest().add(agencyReceivePlan.getPrincipal()));
        agencyReceivePlan.setShouldReceiveDate(borrowerPayPlan.getShouldPayDate());
        agencyReceivePlan.setReceiveStatus(0);
        agencyReceivePlan.setCreateDate(LocalDateTime.now());
        agencyReceivePlan.setCommission(commissionMap.get(borrowerPayPlan.getNumberOfPeriods()));
        // 保存应收明细
        agencyReceivePlanMapper.insert(agencyReceivePlan);
    }

    @Override
    public void doRepay(String date) {
        //1. 查询到期的还款计划, 且payStatus是未还款
        List<BorrowerPayPlan> payPlanList = baseMapper.findPayPlanByDate(date);
        doRepayCommon(payPlanList);
    }

    /**
     * 修改状态
     */
    private void modifyStatus(BorrowerPayPlan borrowerPayPlan) {
        //1. 更新还款明细的状态; borrower_pay_item  status=1
        borrowerPayItemMapper.update(null,
                Wrappers.lambdaUpdate(BorrowerPayItem.class)
                        .set(BorrowerPayItem::getStatus,1)
                        .eq(BorrowerPayItem::getPayPlanId,borrowerPayPlan.getId()));

        //2. 更新应收款计划状态：agency_receive_plan RECEIVE_STATUS=1
        agencyReceivePlanMapper.update(null,
                Wrappers.lambdaUpdate(AgencyReceivePlan.class)
                        .set(AgencyReceivePlan::getReceiveStatus,1)
                        .eq(AgencyReceivePlan::getPayPlanId,borrowerPayPlan.getId()));

        //3. 保存应收明细：agency_receive_item
        //3.1 先根据还款计划id，查询机构应收计划对象
        AgencyReceivePlan agencyReceivePlan = agencyReceivePlanMapper.selectOne(
                Wrappers.lambdaQuery(AgencyReceivePlan.class)
                        .eq(AgencyReceivePlan::getPayPlanId, borrowerPayPlan.getId()));
        //3.2 保存机构实收明细： agency_receive_item
        AgencyReceiveItem agencyReceiveItem = new AgencyReceiveItem();
        agencyReceiveItem.setReceiveId(agencyReceivePlan.getId());
        agencyReceiveItem.setAmount(agencyReceivePlan.getAmount());
        agencyReceiveItem.setReceiveDate(LocalDateTime.now());
        // 保存机构投资实收明细
        agencyReceiveItemMapper.insert(agencyReceiveItem);

        //4. 更新还款计划表：borrower_pay_plan pay_status=1
        borrowerPayPlan.setPayStatus("1");
        this.updateById(borrowerPayPlan);
    }

    /**
     * 生成还款明细 【borrower_pay_item 添加还款明细】
     * @param borrowerPayPlan
     * @return
     */
    private BorrowerPayItem saveBorrowPayItem(BorrowerPayPlan borrowerPayPlan) {

        // 判断：先根据还款计划id查询还款明细，如果不存在就保存还款明细；
        Long borrowerPayPlanId = borrowerPayPlan.getId();
        BorrowerPayItem borrowerPayItem = borrowerPayItemMapper.selectOne(
                Wrappers.lambdaQuery(BorrowerPayItem.class)
                        .eq(BorrowerPayItem::getPayPlanId, borrowerPayPlanId));
        if (borrowerPayItem == null) {
            borrowerPayItem = new BorrowerPayItem();
            borrowerPayItem.setPayPlanId(borrowerPayPlanId);
            borrowerPayItem.setAmount(borrowerPayPlan.getAmount());
            borrowerPayItem.setPayDate(LocalDateTime.now());
            borrowerPayItem.setReqCode(IdGenerator.generator(UserConstant.REQUEST_CODE_PREFIX));
            borrowerPayItem.setStatus(0);
            borrowerPayItemMapper.insert(borrowerPayItem);
        }
        return borrowerPayItem;
    }

    /**
     * 还款预处理，冻结用户还款余额；访问托管代理
     * @param borrowerPayPlan
     * @return
     */
    private Boolean prepareRepay(BorrowerPayPlan borrowerPayPlan,String reqCode) {
        BankBidDTO bankBidDTO = new BankBidDTO();
        bankBidDTO.setAmount(new BigDecimal("0"));
        bankBidDTO.setReqCode(reqCode);
        bankBidDTO.setUserCode(borrowerPayPlan.getUserCode());
        bankBidDTO.setBizType("REPAYMENT");
        bankBidDTO.setBorrowRecordNo(borrowerPayPlan.getBorrowRecordNo());
        bankBidDTO.setId(borrowerPayPlan.getId());
        // 访问代理平台，通过代理访问银行托管系统
        Result result = tradeLogApi.addBankBidAgency(bankBidDTO);
        return result.getCode() == ResultCode.SUCCESS.getCode();
    }

    @Override
    public void doRepay(String date, int shardIndex, int shardTotal) {
        //1. 查询到期的还款计划, 且payStatus是未还款
        List<BorrowerPayPlan> payPlanList = baseMapper.findPayPlanByDateSharding(date,shardIndex,shardTotal);
        log.info("shardIndex:{}",shardIndex);
        log.info("shardTotal:{}",shardTotal);
        log.info("list--->{}",payPlanList);
        doRepayCommon(payPlanList);
    }

    /**
     * 还款通用业务逻辑
     * @param payPlanList
     */
    private void doRepayCommon(List<BorrowerPayPlan> payPlanList) {
        //2. 遍历：生成还款明细、应收款明细、更改还款状态和应收计划的状态
        payPlanList.forEach(borrowerPayPlan -> {
            // 2.1 生成还款明细 【borrower_pay_item 添加还款明细】
            BorrowerPayItem borrowerPayItem = saveBorrowPayItem(borrowerPayPlan);
            // 2.2 还款预处理，冻结用户还款余额；访问托管代理
            Boolean flag = prepareRepay(borrowerPayPlan,borrowerPayItem.getReqCode());
            // 2.3 如果访问托管返回成功，就进行还款确认：修改状态
            if (flag) {
                // 修改状态
                // agency_receive_item 添加收款明细
                // borrower_pay_plan pay_status
                // agency_receive_plan 状态
                this.modifyStatus(borrowerPayPlan);
            }
        });
    }
}
